package com.ds.infra.ibatis.binding;

import com.ds.infra.ibatis.annotation.Param;
import com.ds.infra.ibatis.mapping.MappedStatement;
import com.ds.infra.ibatis.mapping.SqlCommandType;
import com.ds.infra.ibatis.session.Configuration;
import com.ds.infra.ibatis.session.SqlSession;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 作用：封装 MyBatis的Mapper接口中的方法信息，包括方法的SQL命令类型、参数映射等。
 *
 * @author WeiShaoying
 */
@Slf4j
public class MapperMethod {

    private final SqlCommand command;
    private final MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(config, mapperInterface, method);
        this.method = new MethodSignature(config, method);
        log.info("==method:{}", method);
    }

    // public Object execute(SqlSession sqlSession, Object[] args) {
    //     Object result;
    //     switch (command.getType()) {
    //         case INSERT:
    //             Object param = method.convertArgsToSqlCommandParam(args);
    //             result = sqlSession.insert(command.getName(), param);
    //             break;
    //         case UPDATE:
    //             param = method.convertArgsToSqlCommandParam(args);
    //             result = sqlSession.update(command.getName(), param);
    //             break;
    //         case DELETE:
    //             param = method.convertArgsToSqlCommandParam(args);
    //             result = sqlSession.delete(command.getName(), param);
    //             break;
    //         case SELECT:
    //             param = method.convertArgsToSqlCommandParam(args);
    //             if (method.returnsMany()) {
    //                 result = sqlSession.selectList(command.getName(), param);
    //             } else {
    //                 result = sqlSession.selectOne(command.getName(), param);
    //             }
    //             break;
    //         default:
    //             throw new RuntimeException("Unknown execution method for: " + command.getName());
    //     }
    //     return result;
    // }

    /**
     * 作用：执行MyBatis的Mapper接口中的方法，包括参数映射、SQL命令执行等。
     *
     * @param sqlSession MyBatis的SqlSession对象，用于执行SQL命令。
     * @param args       方法参数，用于执行SQL命令。
     * @return 执行结果，根据SQL命令类型不同而返回不同的对象。
     */
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        // 将参数转换为SQL命令的参数
        Object param = method.convertArgsToSqlCommandParam(args);
        //MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());

        switch (command.getType()) {
            case INSERT:
                result = sqlSession.insert(command.getName(), param);
                break;
            case UPDATE:
                result = sqlSession.update(command.getName(), param);
                break;
            case DELETE:
                result = sqlSession.delete(command.getName(), param);
                break;
            case SELECT:
                if (method.returnsMany()) {
                    result = sqlSession.selectList(command.getName(), param);
                } else {
                    result = sqlSession.selectOne(command.getName(), param);
                }
                break;
            default:
                throw new RuntimeException("Unknown execution method for: " + command.getName());
        }
        return result;
    }

    /**
     * 作用： 封装MyBatis的SQL命令信息，包括SQL命令名称、SQL命令类型等。
     *
     * @author WeiShaoying
     */
    @Getter
    public static class SqlCommand {

        private final String name;
        private final SqlCommandType type;

        public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
            String statementName = mapperInterface.getName() + "." + method.getName();
            MappedStatement ms = configuration.getMappedStatement(statementName);
            if (ms == null) {
                throw new RuntimeException("Invalid bound statement (not found): " + statementName);
            }
            // com.ds.infra.ibatis.demo.mapper.UserMapper.insert/com.ds.infra.ibatis.demo.mapper.UserMapper.deleteById
            name = ms.getId();
            // INSERT/DELETE
            type = ms.getSqlCommandType();
            log.info("==Found bound statement ({}): {}", name, type);
        }

    }

    /**
     * 作用： 封装MyBatis的Mapper方法参数信息，包括参数名称、参数类型等
     *
     * @author WeiShaoying
     */
    public static class MethodSignature {

        // 返回值类型
        private final Class<?> returnType;

        // 是否返回多个结果
        private final boolean returnsMany;

        //key: 参数索引, value: 参数名称
        private final SortedMap<Integer, String> params;

        public MethodSignature(Configuration configuration, Method method) {
            this.returnType = method.getReturnType();
            this.returnsMany = List.class.isAssignableFrom(this.returnType);
            this.params = Collections.unmodifiableSortedMap(getParams(method));
        }

        /**
         * 作用： 获取Mapper方法参数信息，包括参数名称和索引。
         *
         * @param method Mapper方法
         * @return 参数名称和索引的映射关系，key为参数索引，value为参数名称
         */
        private SortedMap<Integer, String> getParams(Method method) {
            final SortedMap<Integer, String> params = new TreeMap<>();
            final Annotation[][] paramAnnotations = method.getParameterAnnotations();
            for (int i = 0; i < paramAnnotations.length; i++) {
                String paramName = String.valueOf(i); // 默认使用索引
                for (Annotation annotation : paramAnnotations[i]) {
                    if (annotation instanceof Param) {
                        paramName = ((Param) annotation).value();
                        break;
                    }
                }
                params.put(i, paramName);
            }
            return params;
        }

        public Object convertArgsToSqlCommandParam(Object[] args) {
            if (args == null || args.length == 0) {
                return null;
            } else if (args.length == 1) {
                // 如果是简单类型，直接返回
                if (isSimpleType(args[0].getClass())) {
                    return args[0];
                }
                return args[0];
            } else {
                // 如果是复杂类型，使用Map封装参数
                Map<String, Object> param = new HashMap<>();
                for (Map.Entry<Integer, String> entry : params.entrySet()) {
                    param.put(entry.getValue(), args[entry.getKey()]);
                }
                return param;
            }
        }

        private boolean isSimpleType(Class<?> clazz) {
            return clazz.isPrimitive() ||
                    clazz == String.class ||
                    Number.class.isAssignableFrom(clazz) ||
                    clazz == Boolean.class ||
                    clazz == Character.class;
        }

        public boolean returnsMany() {
            return returnsMany;
        }
    }
}
